package com.acme.bankapp.domain.bank;

import com.acme.bankapp.domain.client.Client;
import com.acme.bankapp.domain.client.Gender;
import com.acme.bankapp.exceptions.ClientExistsException;
import com.acme.bankapp.exceptions.NotEnoughFundsException;
import com.acme.bankapp.networking.email.EmailService;

import java.io.*;
import java.util.*;

public class Bank implements Serializable {

    long serialVersionUID = 1;
    private Set<Client> clients = new HashSet<>();
    transient private List<ClientRegistrationListener> listeners;
    transient private EmailService emailService;

    public Bank() {
        initBankSettings();
    }

    public void initBankSettings() {
        initListeners();
    }

    private void initListeners() {
        listeners = new LinkedList<>();

        addListener(new ClientRegistrationListener() {
            @Override
            public void onClientAdded(Client client) {
                System.out.println(client.getName() + " " + new Date());
            }
        });
        addListener(new PrintClientListener());
        addListener(new EmailNotificationListener());
    }

    @Override
    public String toString() {
        return "Bank{" +
                "clients=" + clients +
                ", listeners=" + listeners +
                '}';
    }

    public void addClient(String name, Gender gender, double amount, double overdraft) throws ClientExistsException, IllegalArgumentException {
        Client newClient = new Client(name, gender, amount, overdraft);
        if (!clients.add(newClient)) {
            throw new ClientExistsException(name);
        }
        for (ClientRegistrationListener listener : listeners) {
            listener.onClientAdded(newClient);
        }
    }

    public void modifyClient(String clientName, double transactionAmount) throws NotEnoughFundsException {
        Client client = getClientFromSet(clientName);
        if (client != null) {
            client.modifyAccount(transactionAmount);
        }
    }

    public double getBalance(String clientName) {
        Client client = getClientFromSet(clientName);
        if (client != null) {
            return client.getBalance();
        }
        return 0;
    }

    public void printMaximumAccountToWithDraw() {
        for (Client client : clients) {
            client.getClientSalutation();
            System.out.println(client);
        }
    }

    public void dumpToFile(File file) throws IOException {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
            for (Client client : clients) {
                // Casting to String is not automatic!!!
                bw.write(client.toString());
                // There is no writeline() method.
                bw.newLine();
            }
        }
    }

    public void addListener(ClientRegistrationListener listener) {
        listeners.add(listener);
    }

    private Client getClientFromSet(String clientName) {
        for (Client client : clients) {
            if (client.getName().equals(clientName)) {
                return client;
            }
        }
        return null;
    }

    private class PrintClientListener implements ClientRegistrationListener {
        @Override
        public void onClientAdded(Client client) {
            client.getClientSalutation();
        }
    }

    private class EmailNotificationListener implements ClientRegistrationListener {
        @Override
        public void onClientAdded(Client client) {
            System.out.println("Notification email for client " + client.getName() + " to be sent");
            emailService = new EmailService();
            emailService.startTransmission();
            emailService.sendNotificationEmail(client, "Address1", "Subject");
            emailService.sendNotificationEmail(client, "Address2", "Subject");
            emailService.sendNotificationEmail(client, "Address3", "Subject");
            emailService.sendNotificationEmail(client, "Address4", "Subject");
            emailService.close();
        }
    }
}
